#include <psptypes.h>
#include <pspmoduleinfo.h>
#include <pspthreadman.h>
#include <pspkernel.h>
#include <pspgu.h>
#include <pspdisplay.h>
#include <pspctrl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <fstream>

//GameMonkey 
#include "GameMonkey/gmConfig.h"
#include "GameMonkey/gmMachine.h"
#include "GameMonkey/gmThread.h"
#include "GameMonkey/gmTableObject.h"
#include "GameMonkey/gmStringObject.h"
#include "GameMonkey/gmHelpers.h"
#include "GameMonkey/gmUtil.h"
#include "GameMonkey/gmStringLib.h"
#include "GameMonkey/gmMathLib.h"
#include "GameMonkey/gmVector3Lib.h"
#include "GameMonkey/gmCall.h"

gmMachine* machine = new gmMachine;
int lastUpdateTime = 0;

static bool running=true;

//End GameMonkey

PSP_MODULE_INFO("PSPGM", 0x0000, 1, 1);

int exit_callback(int arg1, int arg2, void *common) {
	running=false;
   sceKernelExitGame();
   return 0;
}

int CallbackThread(SceSize args, void *argp) {
   int cbid;
   cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
   sceKernelRegisterExitCallback(cbid);
   sceKernelSleepThreadCB();
   return 0;
}

int SetupCallbacks(void) {
   int thid = 0;
   thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
   if(thid >= 0) {
      sceKernelStartThread(thid, 0, 0);
   }
   return thid;
} 

void PrintCallback(gmMachine * a_machine, const char * a_string) {
	pspDebugScreenPrintf(a_string);
}

int GM_CDECL PspDebugPrint(gmThread*a_thread)
{
	GM_CHECK_NUM_PARAMS(1);
	GM_CHECK_STRING_PARAM(message,0);
	pspDebugScreenPrintf(message);
	return GM_OK;
}

int GM_CDECL PspDebugClear(gmThread*a_thread)
{
	pspDebugScreenClear();
	return GM_OK;
}

gmFunctionEntry pspBinds[] =
{
	{"debugPrint",PspDebugPrint},
	{"debugClear",PspDebugClear}
};

void InitGM()
{
	gmMachine::s_printCallback = PrintCallback;
	gmBindStringLib(machine);
	gmBindMathLib(machine);
	gmBindVector3Lib(machine);
	machine->RegisterLibrary(pspBinds, sizeof(pspBinds) / sizeof(pspBinds[0]));

	machine->GetGlobals()->Set(machine, "PSP_CTRL_CROSS", gmVariable(PSP_CTRL_CROSS));
	//gameTable->Set(machine,"GT_FFA", gmVariable(GT_FFA));

	lastUpdateTime=time(0);
}

void ShutdownGM()
{
	delete machine;
	machine=NULL;
}

void UpdateGM()
{
	int deltaTime = time(0) - lastUpdateTime;
	machine->Execute(deltaTime);
	lastUpdateTime=time(0);
}

void LoadScript(const char* file)
{
	FILE *input = fopen(file,"r");
	size_t result;

	fseek( input, 0L, SEEK_END );
	long lSize = ftell( input );
	rewind(input);

	char* script = new char[lSize+1];
	result=fread(script,1,lSize,input);
	if(result != lSize)
	{
		pspDebugScreenPrintf("Error reading file!\n");
		return;
	}
	script[lSize]=0;
	fclose(input);

	int errors = machine->ExecuteString(script, NULL, false, file);
	delete [] script;
	script = NULL;
	if(errors)
	{
		bool first = true; 
		const char *message; 
		while((message = machine->GetLog().GetEntry(first))) { 
			pspDebugScreenPrintf(message);
		} 
		machine->GetLog().Reset();

	}
}

void DoInputStuff(SceCtrlData input,SceCtrlData prevInput)
{
	gmCall  call;
	if(input.Buttons & PSP_CTRL_CROSS && !(prevInput.Buttons & PSP_CTRL_CROSS))
	{//Call Pressed function
		 if(call.BeginGlobalFunction(machine,"ButtonPressed"))
		 {
			 call.AddParamInt(PSP_CTRL_CROSS);
			 call.End();
		 }
	}
	else if(input.Buttons & PSP_CTRL_CROSS && prevInput.Buttons & PSP_CTRL_CROSS)
	{
		 if(call.BeginGlobalFunction(machine,"ButtonHeld"))
		 {
			 call.AddParamInt(PSP_CTRL_CROSS);
			 call.End();
		 }
	}
	else if(!(input.Buttons & PSP_CTRL_CROSS) && prevInput.Buttons & PSP_CTRL_CROSS)
	{
		 if(call.BeginGlobalFunction(machine,"ButtonReleased"))
		 {
			 call.AddParamInt(PSP_CTRL_CROSS);
			 call.End();
		 }
	}
}

int main(void)
{
	pspDebugScreenInit();
	SetupCallbacks();
	InitGM();
	LoadScript("main.gm");
	SceCtrlData pad; 
	SceCtrlData prevPad;

	while(running)
	{
		sceCtrlReadBufferPositive(&pad, 1); 
		DoInputStuff(pad,prevPad);
		UpdateGM();
		prevPad=pad;
	}

	ShutdownGM();
	sceKernelExitGame(); 
	return 0;
}
